
## I've split the processor into different classes covering processor state, addressing modes, operations and op codes.
## These are then combined as mix-ins to the processor state. The main reason for doing this is just to cleanly segment the fields 
## and their associated methods more logically.


STACK_PAGE = 0x0100


class ProcessorState(object):
    """
    implements the CPU's state as well as its knowledge of memory.
    """

    class StatusRegister(object):
        """
        implements the 8-bit status register.
        """

        # C - Carry Flag - last operation caused overflow from bit 7 or underflow from bit 0
        # Z - Zero Flag - last operation was zero
        # I - Interrupt Disable
        # D - Decimal Mode
        # B - Break Command
        # V - Overflow Flag - last operation resulted in an invalid 2's complement result
        # N - Negative Flag - last operation had bit 7 set to one
        

        def __init__(self):
            """
            initialize the status register.
            """
            self.negative = False
            self.overflow = False
            self.break_command = False
            self.decimal_mode = False
            self.interrupt_disable = False
            self.zero = False
            self.carry = False


        def __str__(self):
            """
            pretty print the status register.
            """
            return "N=%s V=%s B=%s D=%s I=%s Z=%s C=%s" % (
                [0, 1][self.negative], 
                [0, 1][self.overflow], 
                [0, 1][self.break_command], 
                [0, 1][self.decimal_mode], 
                [0, 1][self.interrupt_disable], 
                [0, 1][self.zero], 
                [0, 1][self.carry], 
            )


        def update_nz(self, value):
            """
            updates the N and Z flags in light of a particular value.
            """
            self.zero = (value == 0)
            self.negative = ((value & 0x80) != 0)


        def get_byte(self):
            """
            returns the status register as a single byte.
            """
            return self.carry | \
                self.zero << 1 | \
                self.interrupt_disable << 2 | \
                self.decimal_mode << 3 | \
                self.break_command << 4 | \
                1 << 5 | \
                self.overflow << 6 | \
                self.negative << 7

    
        def set_byte(self, status_byte):
            """
            sets the status register from a single byte.
            """
            self.carry = bool(status_byte & 0x01)
            self.zero = bool(status_byte & 0x02)
            self.interrupt_disable = bool(status_byte & 0x04)
            self.decimal_mode = bool(status_byte & 0x08)
            self.break_command = bool(status_byte & 0x10)
            self.overflow = bool(status_byte & 0x40)
            self.negative = bool(status_byte & 0x80)

    
    ## end of StatusRegister class definition
    
    
    def __init__(self, memory):
        """
        initialize the processor state with the given memory.
        """
        self.memory = memory
        
        self.accumulator = 0x00
        self.x_register = 0x00
        self.y_register = 0x00
        
        self.flags = ProcessorState.StatusRegister()

        self.stack_pointer = 0xFF
        
        # the program counter starts off with whatever address is in 0xFFFE
        self.program_counter = self.get_word(0xFFFC)

        # to keep track of cycles, although this isn't being used yet 
        self.cycles = 0


    # the follow allows getting and setting of the status byte as if 'processor_status' were an attribute on the CPU
    def _get_status(self):
        return self.flags.get_byte()
    def _set_status(self, status_byte):
        self.flags.set_byte(status_byte)
    processor_status = property(_get_status, _set_status)
    

    def __str__(self):
        """
        pretty print the processor state.
        """
        return "A=0x%02X X=0x%02X Y=0x%02X %s PC=0x%04X S=0x%02X" % (
            self.accumulator, 
            self.x_register, 
            self.y_register, 
            self.flags, 
            self.program_counter, 
            self.stack_pointer)
    

    def get_word(self, address):
        """
        grabs a little-endian 16-bit value stored at the given address.
        """
        # @@@ not clear where this method belongs
        
        return self.memory[address] + ((self.memory[address + 1]) << 8)


    def push_byte(self, byte):
        """
        push given byte to stack.
        """
        # @@@ not clear where this method belongs

        self.memory[STACK_PAGE + self.stack_pointer] = byte
        self.stack_pointer = (self.stack_pointer - 1) & 0xFF
        

    def pull_byte(self):
        """
        pull byte from stack.
        """
        # @@@ not clear where this method belongs

        self.stack_pointer = (self.stack_pointer + 1) & 0xFF
        return self.memory[STACK_PAGE + self.stack_pointer]


    def push_word(self, word):
        """
        push given word to stack.
        """
        # @@@ not clear where this method belongs

        self.push_byte(word >> 8) # high
        self.push_byte(word & 0xFF) # low


    def pull_word(self):
        """
        pull word from stack.
        """
        # @@@ not clear where this method belongs

        stack_location = STACK_PAGE + self.stack_pointer + 1
        self.stack_pointer += 2
        return self.get_word(stack_location)



class AddressingModes(object):
    """
    implementation of the various addressing modes.
    
    Each method gives, for that addressing mode, the address from which the operation should take its operand.
    """

    def get_pc(self):
        """
        returns the program counter (incrementing it for next time).
        
        The returned value is before the increment.
        """
        pc = self.program_counter
        self.program_counter += 1
        return pc


    ## Implicit and Accumulator mode don't call methods on this class.


    def Immediate(self):
        """
        the operand is to come from the immediate next byte (the address of which is in the program counter).
        """
        return self.get_pc()


    def ZeroPage(self):
        """
        the operand is to come from the zero-page address in the immediate next byte.
        """
        self.cycles += 1
        return self.memory[self.get_pc()]


    def ZeroPageX(self):
        """
        the operand is to come from the the zero-page address in the immediate next byte, offset by the x register.
        """
        self.cycles += 1
        # wraps around
        return (self.memory[self.get_pc()] + self.x_register) & 0xFF


    def ZeroPageY(self):
        """
        the operand is to come from the the zero-page address in the immediate next byte, offset by the y register.
        """
        self.cycles += 1
        # wraps around
        return (self.memory[self.get_pc()] + self.y_register) & 0xFF
        

    def Relative(self):
        """
        the operand is to come from an address relative to the current program counter, offset by a signed amount
        in the immediate next byte.
        """
        # @@@ cycles
        pc = self.get_pc()
        rel = self.memory[pc]
        if rel > 0x7F:
            rel = rel - 0x100 # is this same as 0xFF? @@@
        return pc + 1 + rel    
    

    def Absolute(self):
        """
        the operand is to come from an address in the next two bytes (little-endian).
        """
        return self.memory[self.get_pc()] + (self.memory[self.get_pc()] << 8)
        

    def AbsoluteX(self):
        """
        the operand is to come from an address given in the next two bytes (little-endian), offset by the x register
        """
        return self.memory[self.get_pc()] + (self.memory[self.get_pc()] << 8) + self.x_register
        

    def AbsoluteY(self):
        """
        the operand is to come from an address given in the next two bytes (little-endian), offset by the y register
        """
        return self.memory[self.get_pc()] + (self.memory[self.get_pc()] << 8) + self.y_register
    

    def Indirect(self):
        """
        the operand is to come from an address given in the address given by the next two bytes.
        """
        address = self.Absolute()
        highbyte = (address & 0xFF00) + (((address & 0xFF) + 1) & 0xFF)
        return self.memory[address] + (self.memory[highbyte] << 8)
    

    def IndexedIndirect(self):
        """
        the operand is to come from an address given in (the address given by the next two bytes offset by the x register)
        """
        # zero page wrap around but does it work outside zero page? @@@
        address = (self.memory[self.get_pc()] + self.x_register) & 0xFF
        return self.memory[address] + (self.memory[address + 1] << 8)
    

    def IndirectIndexed(self):
        """
        the operand is to come from an address given in (the address given by the next two bytes) offset by the y register
        """
        address = self.memory[self.get_pc()]
        return self.memory[address] + (self.memory[address + 1] << 8) + self.y_register

        

## KNOWN BUGS IN REAL 6502 THAT WE HAVE TO EMULATE:
# JMP <xxFF> jumps not to xxFF xxFF+1 but xxFF xx00
# NMI and IRQ simultaneously causes latter to be ignored
# N V and Z are invalid after arithmetic operating in BCD mode


class Operations:


    ## Load / Store Operations


    def LDA(self, op):
        """
        LoaD Accumulator.
        """
        self.cycles += 1
        self.accumulator = self.memory[op]
        self.flags.update_nz(self.accumulator)


    def LDX(self, op):
        """
        LoaD X Register.
        """
        self.cycles += 1
        self.x_register = self.memory[op]
        self.flags.update_nz(self.x_register)


    def LDY(self, op):
        """
        LoaD Y Register.
        """
        self.cycles += 1
        self.y_register = self.memory[op]
        self.flags.update_nz(self.y_register)

    def STA(self, op):
        """
        STore Accumulator.
        """
        self.cycles += 1
        self.memory[op] = self.accumulator & 0xFF


    def STX(self, op):
        """
        STore X Register.
        """
        self.cycles += 1
        self.memory[op] = self.x_register & 0xFF


    def STY(self, op):
        """
        STore Y Register.
        """
        self.cycles += 1
        self.memory[op] = self.y_register & 0xFF
    

    ## Register Transfers

    
    def TAX(self):
        """
        Transfer Accumulator to X.
        """
        self.x_register = self.accumulator
        self.flags.update_nz(self.x_register)


    def TAY(self):
        """
        Transfer Accumulator to Y.
        """
        self.y_register = self.accumulator
        self.flags.update_nz(self.y_register)


    def TXA(self):
        """
        Transfer X to Accumulator.
        """
        self.accumulator = self.x_register
        self.flags.update_nz(self.accumulator)


    def TYA(self):
        """
        Transfer Y to Accumulator.
        """
        self.accumulator = self.y_register
        self.flags.update_nz(self.accumulator)
    

    ## Stack Operations


    def TSX(self):
        """
        Transfer Stack pointer to X.
        """
        self.x_register = self.stack_pointer
        self.flags.update_nz(self.x_register)

    
    def TXS(self):
        """
        Transfer X to Stack pointer.
        """
        self.stack_pointer = self.x_register
    

    def PHA(self):
        """
        PusH Accumulator on stack.
        """
        self.cycles += 1
        self.push_byte(self.accumulator)


    def PHP(self):
        """
        PusH Processor status on stack.
        """
        self.cycles += 1
        self.push_byte(self.processor_status)
    

    def PLA(self):
        """
        PuLl Accumulator from stack.
        """
        self.cycles += 2
        self.accumulator = self.pull_byte()
        self.flags.update_nz(self.accumulator)
    

    def PLP(self):
        """
        PuLl Processor status from stack.
        """
        self.cycles += 2
        self.processor_status = self.pull_byte()
    

    ## Logical

    
    def AND(self, op):
        """
        logical AND.
        """
        self.cycles += 1
        self.accumulator &= self.memory[op] & 0xFF
        self.flags.update_nz(self.accumulator)

    
    def EOR(self, op):
        """
        Exclusive OR.
        """
        self.accumulator ^= self.memory[op] & 0xFF
        self.flags.update_nz(self.accumulator)
    

    def ORA(self, op):
        """
        logical inclusive OR.
        """
        self.cycles += 1
        self.accumulator |= self.memory[op] & 0xFF
        self.flags.update_nz(self.accumulator)
    

    def BIT(self, op):
        """
        BIt Test.
        """
        self.cycles += 1
        value = self.memory[op] & 0xFF
        self.flags.overflow = ((value & 0x40) != 0)
        self.flags.negative = ((value & 0x80) != 0)
        self.flags.zero = ((value & self.accumulator) == 0)
    

    ## Arithmetic

    
    def ADC(self, op):
        """
        ADd with Carry.
        """
        self.cycles += 1
        # TODO doesn't handle BCD yet
        if self.flags.decimal_mode:
            raise Exception, "BCD NYI"

        a1 = a2 = self.accumulator
        if a1 & 0x80:
            a1 = (a1 & 0x7F) - 0x80
        m1 = m2 = self.memory[op]
        if m1 & 0x80:
            m1 = (m1 & 0x7F) - 0x80

        # result1 is the twos complement addition
        result1 = a1 + m1 + [0, 1][self.flags.carry]
        
        # result2 is the unsigned addition
        result2 = a2 + m2 + [0, 1][self.flags.carry]
        
        self.accumulator = result2 & 0xFF
        self.flags.carry = (result2 > 0xFF)
        self.flags.update_nz(self.accumulator)
        
        # @@@ perhaps this could be calculated from result2 but result1 is more intuitive
        self.flags.overflow = (result1 > 127) | (result1 < -128)
    

    def SBC(self, op):
        """
        SuBtract with Carry.
        """
        self.cycles += 1
        # TODO doesn't handle BCD yet
        if self.flags.decimal_mode:
            raise Exception, "BCD NYI"

        a1 = a2 = self.accumulator
        if a1 & 0x80:
            a1 = (a1 & 0x7F) - 0x80
        m1 = m2 = self.memory[op]
        if m1 & 0x80:
            m1 = (m1 & 0x7F) - 0x80

        # result1 is the twos complement addition
        result1 = a1 - m1 - [1, 0][self.flags.carry]
        
        # result2 is the unsigned addition
        result2 = a2 - m2 - [1, 0][self.flags.carry]
        
        self.accumulator = result2 & 0xFF
        self.flags.carry = (result2 >= 0)
        self.flags.update_nz(self.accumulator)

        # @@@ perhaps this could be calculated from result2 but result1 is more intuitive
        self.flags.overflow = (result1 > 127) | (result1 < -128)

    
    def CMP(self, op):
        """
        CoMPare accumulator.
        """
        self.cycles += 1

        result = self.accumulator - self.memory[op]

        self.flags.carry = (result >= 0)
        self.flags.update_nz(result & 0xFF)

    
    def CPX(self, op):
        """
        ComPare X register.
        """
        self.cycles += 1

        result = self.x_register - self.memory[op]

        self.flags.carry = (result >= 0)
        self.flags.update_nz(result & 0xFF)


    def CPY(self, op):
        """
        ComPare Y register.
        """
        self.cycles += 1

        result = self.y_register - self.memory[op]

        self.flags.carry = (result >= 0)
        self.flags.update_nz(result & 0xFF)
    

    ## Increments & Decrements

    
    def INC(self, op):
        """
        INCrement a memory location.
        """
        self.cycles += 2
        m = (self.memory[op] + 1) & 0xFF
        self.flags.update_nz(m)
        self.memory[op] = m

    
    def INX(self):
        """
        INcrement the X register.
        """
        x = (self.x_register + 1) & 0xFF
        self.flags.update_nz(x)
        self.x_register = x

    
    def INY(self):
        """
        INcrement the Y register.
        """
        y = (self.y_register + 1) & 0xFF
        self.flags.update_nz(y)
        self.y_register = y

    
    def DEC(self, op):
        """
        DECrement a memory location.
        """
        self.cycles += 2
        m = (self.memory[op] - 1) & 0xFF
        self.flags.update_nz(m)
        self.memory[op] = m

    
    def DEA(self):
        """
        DEcrement the Accumulator.
        """
        a = (self.accumulator - 1) & 0xFF
        self.flags.update_nz(a)
        self.accumulator = a

    
    def DEX(self):
        """
        DEcrement the X register.
        """
        x = (self.x_register - 1) & 0xFF
        self.flags.update_nz(x)
        self.x_register = x

    
    def DEY(self):
        """
        DEcrement the Y register.
        """
        y = (self.y_register - 1) & 0xFF
        self.flags.update_nz(y)
        self.y_register = y
    

    ## Shifts

    
    def ASL(self, op=None):
        """
        Arithmetic Shift Left.
        """
        if op:
            m = self.memory[op] << 1
            self.flags.update_nz(m)
            self.flags.carry = (m > 0xFF)
            self.memory[op] = m & 0xFF
        else:
            a = self.accumulator << 1
            self.flags.update_nz(a)
            self.flags.carry = (a > 0xFF)
            self.accumulator = a & 0xFF

    
    def LSR(self, op=None):
        """
        Logical Shift Right.
        """
        if op:
            self.flags.carry = self.memory[op] % 2
            self.memory[op] = self.memory[op] >> 1
            self.flags.update_nz(self.memory[op])
        else:
            self.flags.carry = self.accumulator % 2
            self.accumulator = self.accumulator >> 1
            self.flags.update_nz(self.accumulator)

    
    def ROL(self, op=None):
        """
        ROtate Left.
        """
        if op:
            m = self.memory[op] << 1
            if self.flags.carry:
                m = m | 0x01
            self.flags.update_nz(m)
            self.flags.carry = (m > 0xFF)
            self.memory[op] = m & 0xFF
        else:
            a = self.accumulator << 1
            if self.flags.carry:
                a = a | 0x01
            self.flags.update_nz(a)
            self.flags.carry = (a > 0xFF)
            self.accumulator = a & 0xFF

    
    def ROR(self, op=None):
        """
        ROtate Right.
        """
        if op:
            if self.flags.carry:
                self.memory[op] = self.memory[op] | 0x100
            self.flags.carry = self.memory[op] % 2
            m = self.memory[op] >> 1
            self.flags.update_nz(m)
            self.memory[op] = m
        else:
            if self.flags.carry:
                self.accumulator = self.accumulator | 0x100
            self.flags.carry = self.accumulator % 2
            a = self.accumulator >> 1
            self.flags.update_nz(a)
            self.accumulator = a
    

    ## Jumps & Calls

    
    def JMP(self, op):
        """
        JuMP to another location.
        """
        self.program_counter = op

    
    def JSR(self, op):
        """
        Jump to a SubRoutine.
        """
        self.cycles += 3
        self.push_word(self.program_counter - 1)
        self.program_counter = op

        
    def RTS(self):
        """
        ReTurn from Subroutine.
        """
        self.cycles += 2
        self.program_counter = self.pull_word() + 1
            

    ## Branches

    
    def BCC(self, op):
        """
        Branch if Carry flag Clear.
        """
        if not self.flags.carry:
            self.program_counter = op

    
    def BCS(self, op):
        """
        Branch if Carry flag Set.
        """
        if self.flags.carry:
            self.program_counter = op

    
    def BEQ(self, op):
        """
        Branch on EQual (zero flag set).
        """
        if self.flags.zero:
            self.program_counter = op

    
    def BMI(self, op):
        """
        Branch on MInus (negative flag set).
        """
        if self.flags.negative:
            self.program_counter = op

    
    def BNE(self, op):
        """
        Branch on Not Equal (zero flag clear).
        """
        if not self.flags.zero:
            self.program_counter = op

    
    def BPL(self, op):
        """
        Branch on PLus (negative flag clear).
        """
        if not self.flags.negative:
            self.program_counter = op

    
    def BVC(self, op):
        """
        Branch on oVerflow Clear.
        """
        if not self.flags.overflow:
            self.program_counter = op

    
    def BVS(self, op):
        """
        Branch on oVerflow Set.
        """
        if self.flags.overflow:
            self.program_counter = op
    

    ## Status Flag Changes

    
    def CLC(self):
        """
        CLear Carry flag.
        """
        self.flags.carry = False
    
    def CLD(self):
        """
        CLear Decimal mode flag.
        """
        self.flags.decimal_mode = False
    
    def CLI(self):
        """
        CLear Interrupt disable flag.
        """
        self.flags.interrupt_disable = False
    
    def CLV(self):
        """
        CLear oVerflow flag.
        """
        self.flags.overflow = False
    
    def SEC(self):
        """
        SEt Carry flag.
        """
        self.flags.carry = True
    
    def SED(self):
        """
        SEt Decimal mode flag.
        """
        self.flags.decimal_mode = True
    
    def SEI(self):
        """
        SEt Interrupt disable flag
        """
        self.flags.interrupt_disable = True
    

    ## System Functions

    
    def BRK(self):
        """
        BReaK.
        """
        self.push_word(self.program_counter + 1)
        self.push_byte(self.processor_status)
        self.program_counter = self.get_word(0xFFFE)
        self.flags.break_command = True

        
    def NOP(self):
        """
        No OPeration.
        """
        pass

        
    def RTI(self):
        """
        ReTurn from Interrupt.
        """
        self.cycles += 2
        self.processor_status = self.pull_byte()
        self.program_counter = self.pull_word()

    

class OpCodes:


    def setup_opcodes(self):
        self.op_codes = {
            0x00: lambda: self.BRK(), 
            0x01: lambda: self.ORA(self.IndexedIndirect()), 
            0x05: lambda: self.ORA(self.ZeroPage()), 
            0x06: lambda: self.ASL(self.ZeroPage()), 
            0x08: lambda: self.PHP(), 
            0x09: lambda: self.ORA(self.Immediate()), 
            0x0A: lambda: self.ASL(), 
            0x0D: lambda: self.ORA(self.Absolute()), 
            0x0E: lambda: self.ASL(self.Absolute()), 
            0x10: lambda: self.BPL(self.Relative()), 
            0x11: lambda: self.ORA(self.IndirectIndexed()), 
            0x15: lambda: self.ORA(self.ZeroPageX()), 
            0x16: lambda: self.ASL(self.ZeroPageX()), 
            0x18: lambda: self.CLC(), 
            0x19: lambda: self.ORA(self.AbsoluteY()), 
            0x1D: lambda: self.ORA(self.AbsoluteX()), 
            0x1E: lambda: self.ASL(self.AbsoluteX()), 
            0x20: lambda: self.JSR(self.Absolute()), 
            0x21: lambda: self.AND(self.IndexedIndirect()), 
            0x24: lambda: self.BIT(self.ZeroPage()), 
            0x25: lambda: self.AND(self.ZeroPage()), 
            0x26: lambda: self.ROL(self.ZeroPage()), 
            0x28: lambda: self.PLP(), 
            0x29: lambda: self.AND(self.Immediate()), 
            0x2A: lambda: self.ROL(), 
            0x2C: lambda: self.BIT(self.Absolute()), 
            0x2D: lambda: self.AND(self.Absolute()), 
            0x2E: lambda: self.ROL(self.Absolute()), 
            0x30: lambda: self.BMI(self.Relative()), 
            0x31: lambda: self.AND(self.IndirectIndexed()), 
            0x35: lambda: self.AND(self.ZeroPageX()), 
            0x36: lambda: self.ROL(self.ZeroPageX()), 
            0x38: lambda: self.SEC(), 
            0x39: lambda: self.AND(self.AbsoluteY()), 
            0x3A: lambda: self.DEA(), # new instruction for 65C02
            0x3D: lambda: self.AND(self.AbsoluteX()), 
            0x3E: lambda: self.ROL(self.AbsoluteX()), 
            0x40: lambda: self.RTI(), 
            0x41: lambda: self.EOR(self.IndexedIndirect()), 
            0x45: lambda: self.EOR(self.ZeroPage()), 
            0x46: lambda: self.LSR(self.ZeroPage()), 
            0x48: lambda: self.PHA(), 
            0x49: lambda: self.EOR(self.Immediate()), 
            0x4A: lambda: self.LSR(), 
            0x4C: lambda: self.JMP(self.Absolute()), 
            0x4D: lambda: self.EOR(self.Absolute()), 
            0x4E: lambda: self.LSR(self.Absolute()), 
            0x50: lambda: self.BVC(self.Relative()), 
            0x51: lambda: self.EOR(self.IndirectIndexed()), 
            0x55: lambda: self.EOR(self.ZeroPageX()), 
            0x56: lambda: self.LSR(self.ZeroPageX()), 
            0x58: lambda: self.CLI(), 
            0x59: lambda: self.EOR(self.AbsoluteY()), 
            0x5D: lambda: self.EOR(self.AbsoluteX()), 
            0x5E: lambda: self.LSR(self.AbsoluteX()), 
            0x60: lambda: self.RTS(), 
            0x61: lambda: self.ADC(self.IndexedIndirect()), 
            0x65: lambda: self.ADC(self.ZeroPage()), 
            0x66: lambda: self.ROR(self.ZeroPage()), 
            0x68: lambda: self.PLA(), 
            0x69: lambda: self.ADC(self.Immediate()), 
            0x6A: lambda: self.ROR(), 
            0x6C: lambda: self.JMP(self.Indirect()), 
            0x6D: lambda: self.ADC(self.Absolute()), 
            0x6E: lambda: self.ROR(self.Absolute()), 
            0x70: lambda: self.BVS(self.Relative()), 
            0x71: lambda: self.ADC(self.IndirectIndexed()), 
            0x75: lambda: self.ADC(self.ZeroPageX()), 
            0x76: lambda: self.ROR(self.ZeroPageX()), 
            0x78: lambda: self.SEI(), 
            0x79: lambda: self.ADC(self.AbsoluteY()), 
            0x7D: lambda: self.ADC(self.AbsoluteX()), 
            0x7E: lambda: self.ROR(self.AbsoluteX()), 
            0x81: lambda: self.STA(self.IndexedIndirect()), 
            0x84: lambda: self.STY(self.ZeroPage()), 
            0x85: lambda: self.STA(self.ZeroPage()), 
            0x86: lambda: self.STX(self.ZeroPage()), 
            0x88: lambda: self.DEY(), 
            0x8A: lambda: self.TXA(), 
            0x8C: lambda: self.STY(self.Absolute()), 
            0x8D: lambda: self.STA(self.Absolute()), 
            0x8E: lambda: self.STX(self.Absolute()), 
            0x90: lambda: self.BCC(self.Relative()), 
            0x91: lambda: self.STA(self.IndirectIndexed()), 
            0x94: lambda: self.STY(self.ZeroPageX()), 
            0x95: lambda: self.STA(self.ZeroPageX()), 
            0x96: lambda: self.STX(self.ZeroPageY()), 
            0x98: lambda: self.TYA(), 
            0x99: lambda: self.STA(self.AbsoluteY()), 
            0x9A: lambda: self.TXS(), 
            0x9D: lambda: self.STA(self.AbsoluteX()), 
            0xA0: lambda: self.LDY(self.Immediate()), 
            0xA1: lambda: self.LDA(self.IndexedIndirect()), 
            0xA2: lambda: self.LDX(self.Immediate()), 
            0xA4: lambda: self.LDY(self.ZeroPage()), 
            0xA5: lambda: self.LDA(self.ZeroPage()), 
            0xA6: lambda: self.LDX(self.ZeroPage()), 
            0xA8: lambda: self.TAY(), 
            0xA9: lambda: self.LDA(self.Immediate()), 
            0xAA: lambda: self.TAX(), 
            0xAC: lambda: self.LDY(self.Absolute()), 
            0xAD: lambda: self.LDA(self.Absolute()), 
            0xAE: lambda: self.LDX(self.Absolute()), 
            0xB0: lambda: self.BCS(self.Relative()), 
            0xB1: lambda: self.LDA(self.IndirectIndexed()), 
            0xB4: lambda: self.LDY(self.ZeroPageX()), 
            0xB5: lambda: self.LDA(self.ZeroPageX()), 
            0xB6: lambda: self.LDX(self.ZeroPageY()), 
            0xB8: lambda: self.CLV(), 
            0xB9: lambda: self.LDA(self.AbsoluteY()), 
            0xBA: lambda: self.TXS(), 
            0xBC: lambda: self.LDY(self.AbsoluteX()), 
            0xBD: lambda: self.LDA(self.AbsoluteX()), 
            0xBE: lambda: self.LDX(self.AbsoluteY()), 
            0xC0: lambda: self.CPY(self.Immediate()), 
            0xC1: lambda: self.CMP(self.IndexedIndirect()), 
            0xC4: lambda: self.CPY(self.ZeroPage()), 
            0xC5: lambda: self.CMP(self.ZeroPage()), 
            0xC6: lambda: self.DEC(self.ZeroPage()), 
            0xC8: lambda: self.INY(), 
            0xC9: lambda: self.CMP(self.Immediate()), 
            0xCA: lambda: self.DEX(), 
            0xCC: lambda: self.CPY(self.Absolute()), 
            0xCD: lambda: self.CMP(self.Absolute()), 
            0xCE: lambda: self.DEC(self.Absolute()), 
            0xD0: lambda: self.BNE(self.Relative()), 
            0xD1: lambda: self.CMP(self.IndirectIndexed()), 
            0xD5: lambda: self.CMP(self.ZeroPageX()), 
            0xD6: lambda: self.DEC(self.ZeroPageX()), 
            0xD8: lambda: self.CLD(), 
            0xD9: lambda: self.CMP(self.AbsoluteY()), 
            0xDD: lambda: self.CMP(self.AbsoluteX()), 
            0xDE: lambda: self.DEC(self.AbsoluteX()), 
            0xE0: lambda: self.CPX(self.Immediate()), 
            0xE1: lambda: self.SBC(self.IndexedIndirect()), 
            0xE4: lambda: self.CPX(self.ZeroPage()), 
            0xE5: lambda: self.SBC(self.ZeroPage()), 
            0xE6: lambda: self.INC(self.ZeroPage()), 
            0xE8: lambda: self.INX(), 
            0xE9: lambda: self.SBC(self.Immediate()), 
            0xEA: lambda: self.NOP(), 
            0xEC: lambda: self.CPX(self.Absolute()), 
            0xED: lambda: self.SBC(self.Absolute()), 
            0xEE: lambda: self.INC(self.Absolute()), 
            0xF0: lambda: self.BEQ(self.Relative()), 
            0xF1: lambda: self.SBC(self.IndirectIndexed()), 
            0xF5: lambda: self.SBC(self.ZeroPageX()), 
            0xF6: lambda: self.INC(self.ZeroPageX()), 
            0xF8: lambda: self.SED(), 
            0xF9: lambda: self.SBC(self.AbsoluteY()), 
            0xFD: lambda: self.SBC(self.AbsoluteX()), 
            0xFE: lambda: self.INC(self.AbsoluteX())
        }



class CPU(ProcessorState, AddressingModes, Operations, OpCodes):


    def main_loop(self):
        self.power = True
        self.RESET = True
        self.NMI = False
        self.IRQ = False
        
        self.setup_opcodes()
        
        while self.power:
            self.memory.callback()
            if self.RESET:
                self.stack_pointer = 0xFF
                self.program_counter = self.get_word(0xFFFC)
                self.RESET = False
                self.NMI = False
                self.IRQ = False
            elif self.NMI:
                self.program_counter = self.get_word(0xFFFA)
                self.NMI = False
                self.IRQ = False
            elif self.IRQ:
                self.program_counter = self.get_word(0xFFFE)
                self.IRQ = False
            print self
            pc = self.get_pc()
            opcode = self.memory[pc]
            if opcode not in self.op_codes:
                print "opcode", hex(opcode).upper(), "not defined"
                import sys; sys.exit(1)
            self.op_codes[opcode]()


if __name__ == "__main__":
    import memory
    m = memory.Memory(0x10000)
    m.load_file(0xD000, "APPLE2.ROM")
    cpu = CPU(m)
    cpu.main_loop()